#include "ClockManager.h"
#include <math.h>
#include "fonts.h"

ClockManager::ClockManager(Panel& panel, NTPTime& ntpTime) 
    : _panel(panel), _ntpTime(ntpTime) {
}

bool ClockManager::updateIfNeeded(unsigned long& lastUpdate, unsigned long intervalMs, int rightTzOffsetHours, bool otaInProgress) {
    if (millis() - lastUpdate <= intervalMs) return false;
    lastUpdate = millis();
    
    // Skip drawing if OTA is in progress
    if (otaInProgress) {
        return false; // Don't draw clocks during OTA
    }
    
    drawClock(rightTzOffsetHours, otaInProgress);
    return true;
}

void ClockManager::drawClock(int rightTzOffsetHours, bool otaInProgress) {
    // Absolute safety check: never draw clocks during OTA
    if (otaInProgress) {
        return; // Block all clock drawing during OTA
    }
    
    // Get local time for left panel
    uint8_t hourL, minuteL, secondL, wdayL;
    const char* weekDayStrL = "";
    int yearL = 0, monthL = 0, dayL = 0;
    
    getTimeForTimezone(0, hourL, minuteL, secondL, wdayL, yearL, monthL, dayL, weekDayStrL);
    
    // Draw the clock in the left square area (local time)
    drawAnalogClock(0, 0, _panel.height, _panel.height, hourL, minuteL, secondL, weekDayStrL, yearL, monthL, dayL, "SH");

    // Get time for right panel timezone
    uint8_t hourR = 0, minuteR = 0, secondR = 0, wdayR = 0;
    int yearR = 0, monthR = 0, dayR = 0;
    const char* weekDayStrR = "";
    bool haveR = false;
    
    if (_ntpTime.isTimeValid()) {
        if (rightTzOffsetHours == -8) {
            // Use DST-aware Pacific Time helper
            haveR = _ntpTime.getPacificTime(hourR, minuteR, secondR, wdayR, yearR, monthR, dayR);
        } else {
            haveR = _ntpTime.getTimeForOffset(rightTzOffsetHours, hourR, minuteR, secondR, wdayR, yearR, monthR, dayR);
        }
        
        if (haveR) {
            static const char* WEEKDAYS[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
            weekDayStrR = WEEKDAYS[wdayR % 7];
        }
    } else {
        // Fallback for right panel when no NTP
        unsigned long total = millis() / 1000;
        time_t base = total + rightTzOffsetHours * 3600;
        struct tm t;
        gmtime_r(&base, &t);
        hourR = t.tm_hour; minuteR = t.tm_min; secondR = t.tm_sec; wdayR = t.tm_wday;
        yearR = t.tm_year + 1900; monthR = t.tm_mon + 1; dayR = t.tm_mday;
        static const char* WEEKDAYS[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
        weekDayStrR = WEEKDAYS[wdayR % 7];
        haveR = true;
    }
    
    // Draw right panel if we have space and time data
    if (haveR) {
        int rightX = _panel.width - _panel.height;
        if (rightX > 0) {
            const char* rightLocation = (rightTzOffsetHours == -8) ? "SF" : "TZ";
            drawAnalogClock(rightX, 0, _panel.height, _panel.height, hourR, minuteR, secondR, weekDayStrR, yearR, monthR, dayR, rightLocation);
        }
    }

    // Serial debug output
    Serial.printf("Left(local): %04d-%02d-%02d %02d:%02d:%02d %s\n", yearL, monthL, dayL, hourL, minuteL, secondL, weekDayStrL);
    if (haveR) {
        if (rightTzOffsetHours == -8) {
            Serial.printf("Right(Pacific): %04d-%02d-%02d %02d:%02d:%02d %s\n", yearR, monthR, dayR, hourR, minuteR, secondR, weekDayStrR);
        } else {
            Serial.printf("Right(offset %d): %04d-%02d-%02d %02d:%02d:%02d %s\n", rightTzOffsetHours, yearR, monthR, dayR, hourR, minuteR, secondR, weekDayStrR);
        }
    } else {
        Serial.println("Right: time unavailable");
    }
}

void ClockManager::getTimeForTimezone(int tzOffsetHours, uint8_t& hour, uint8_t& minute, uint8_t& second, 
                                     uint8_t& wday, int& year, int& month, int& day, const char*& weekDayStr) {
    static const char* WEEKDAYS[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
    
    if (_ntpTime.isTimeValid()) {
        // Use configured local time getters for timezone 0 (local)
        if (tzOffsetHours == 0) {
            hour = _ntpTime.getHour();
            minute = _ntpTime.getMinute();
            second = _ntpTime.getSecond();
            time_t now = time(nullptr);
            struct tm timeinfo;
            localtime_r(&now, &timeinfo);
            wday = timeinfo.tm_wday; // 0=Sunday
            year = timeinfo.tm_year + 1900;
            month = timeinfo.tm_mon + 1;
            day = timeinfo.tm_mday;
        } else {
            // For other timezones, use offset methods
            _ntpTime.getTimeForOffset(tzOffsetHours, hour, minute, second, wday, year, month, day);
        }
        weekDayStr = WEEKDAYS[wday % 7];
    } else {
        getFallbackTime(hour, minute, second, wday, year, month, day, weekDayStr);
    }
}

void ClockManager::getFallbackTime(uint8_t& hour, uint8_t& minute, uint8_t& second, 
                                  uint8_t& wday, int& year, int& month, int& day, const char*& weekDayStr) {
    static const char* WEEKDAYS[] = {"SUN", "MON", "TUE", "WED", "THU", "FRI", "SAT"};
    
    unsigned long totalSeconds = millis() / 1000;
    second = totalSeconds % 60;
    minute = (totalSeconds / 60) % 60;
    hour = (totalSeconds / 3600) % 24;
    wday = (totalSeconds / 86400 + 4) % 7; // fallback weekday
    
    // Crude fallback date (days since epoch)
    unsigned long days = totalSeconds / 86400;
    year = 1970;
    month = 1;
    day = (days % 30) + 1;
    
    weekDayStr = WEEKDAYS[wday % 7];
}

void ClockManager::drawText5x8(const char* text, uint16_t x, uint16_t y, uint8_t r, uint8_t g, uint8_t b) {
    uint8_t textLen = 0;
    for (const char* p = text; *p; ++p) ++textLen;
    
    uint8_t charSpacing = 1;
    for (uint8_t i = 0; i < textLen; ++i) {
        int idx = fontIndex(text[i]);
        for (uint8_t row = 0; row < 8; ++row) {
            uint8_t bits = font5x8[idx][row];
            for (uint8_t col = 0; col < 5; ++col) {
                if (bits & (1 << (4 - col))) {
                    _panel.drawPixel(x + i * (5 + charSpacing) + col, y + row, r, g, b);
                }
            }
        }
    }
}

void ClockManager::drawAnalogClock(uint16_t posX, uint16_t posY, uint16_t w, uint16_t h,
                                  uint8_t hour, uint8_t minute, uint8_t second,
                                  const char* weekDayStr, int year, int month, int day,
                                  const char* locationStr) {
    // Compute center and radius inside bounding box
    uint16_t cx = posX + w / 2;
    uint16_t cy = posY + h / 2;
    uint16_t radius = (w < h ? w : h) / 2;
    if (radius > 2) radius -= 2;  // small margin

    // Clear the clock area
    if (w > 0 && h > 0) {
        _panel.fillRect(posX, posY, posX + w - 1, posY + h - 1, 0, 0, 0);
    }

    // Draw outer circle
    _panel.drawCircle(cx, cy, radius, 100, 100, 0);

    // Draw hour ticks
    for (int i = 0; i < 12; i++) {
        float angle = (i * 30.0f) * M_PI / 180.0f;  // degrees to radians
        int16_t sx = cx + (int16_t)((radius - 4) * cos(angle));
        int16_t sy = cy + (int16_t)((radius - 4) * sin(angle));
        int16_t ex = cx + (int16_t)(radius * cos(angle));
        int16_t ey = cy + (int16_t)(radius * sin(angle));
        _panel.drawLine(sx, sy, ex, ey, 100, 100, 0);
    }

    // Draw week day string at the midpoint between 12 o'clock and the center
    if (weekDayStr) {
        uint8_t textLen = 0;
        for (const char* p = weekDayStr; *p; ++p) ++textLen;
        uint8_t charSpacing = 1;
        uint8_t textWidth = (5 + charSpacing) * textLen - charSpacing;
        uint8_t textHeight = 8;
        int16_t midY = (cy - radius + cy) / 2;
        int16_t textX = cx - textWidth / 2;
        int16_t textY = midY - textHeight / 2;
        
        for (uint8_t i = 0; i < textLen; ++i) {
            int idx = fontIndex(weekDayStr[i]);
            for (uint8_t row = 0; row < 8; ++row) {
                uint8_t bits = font5x8[idx][row];
                for (uint8_t col = 0; col < 5; ++col) {
                    if (bits & (1 << (4 - col))) {
                        _panel.drawPixel(textX + i * (5 + charSpacing) + col, textY + row, 0, 100, 0);
                    }
                }
            }
        }
    }

    // Draw date string below the center
    char dateStr[12] = "";
    if (year > 0 && month > 0) {
        snprintf(dateStr, sizeof(dateStr), "%04d-%02d", year, month);
    } else {
        snprintf(dateStr, sizeof(dateStr), "\?\?\?\?-\?\?");
    }
    uint8_t dateLen = 0;
    for (const char* p = dateStr; *p; ++p) ++dateLen;
    uint8_t charSpacing = 1;
    uint8_t dateWidth = (5 + charSpacing) * dateLen - charSpacing;
    uint8_t dateHeight = 8;
    int16_t midY2 = cy + (radius * 0.5f) - 3;
    int16_t dateX = cx - dateWidth / 2;
    int16_t dateY = midY2 - dateHeight / 2;
    
    for (uint8_t i = 0; i < dateLen; ++i) {
        int idx = fontIndex(dateStr[i]);
        for (uint8_t row = 0; row < 8; ++row) {
            uint8_t bits = font5x8[idx][row];
            for (uint8_t col = 0; col < 5; ++col) {
                if (bits & (1 << (4 - col))) {
                    _panel.drawPixel(dateX + i * (5 + charSpacing) + col, dateY + row, 0, 100, 0);
                }
            }
        }
    }

    // Draw location abbreviation below the date
    if (locationStr) {
        uint8_t locLen = 0;
        for (const char* p = locationStr; *p; ++p) ++locLen;
        uint8_t charSpacing = 1;
        uint8_t locWidth = (5 + charSpacing) * locLen - charSpacing;
        int16_t locY = dateY + dateHeight + 2;
        int16_t locX = cx - locWidth / 2;
        
        for (uint8_t i = 0; i < locLen; ++i) {
            int idx = fontIndex(locationStr[i]);
            for (uint8_t row = 0; row < 8; ++row) {
                uint8_t bits = font5x8[idx][row];
                for (uint8_t col = 0; col < 5; ++col) {
                    if (bits & (1 << (4 - col))) {
                        _panel.drawPixel(locX + i * (5 + charSpacing) + col, locY + row, 0, 100, 0);
                    }
                }
            }
        }
    }

    // Draw day of month at midpoint between center and 9 o'clock with 1.5x scaling
    int dayVal = (day > 0) ? day : 1;
    char dayStr[3];
    if (dayVal < 10) {
        snprintf(dayStr, sizeof(dayStr), "%d", dayVal);
    } else {
        snprintf(dayStr, sizeof(dayStr), "%02d", dayVal);
    }
    uint8_t numDigits = (dayVal < 10) ? 1 : 2;
    int16_t midX_day = (cx + (cx - radius)) / 2;
    float scale = 1.5f;
    uint8_t charW = 5 * scale;
    uint8_t charH = 8 * scale;
    uint8_t charSpacingDay = 3 * scale;
    uint8_t dayWidth = (charW + charSpacingDay) * numDigits - charSpacingDay;
    int16_t dayX = midX_day - dayWidth / 2;
    int16_t dayY = cy - charH / 2;
    
    for (uint8_t i = 0; i < numDigits; ++i) {
        int idx = fontIndex(dayStr[i]);
        for (uint8_t row = 0; row < 8; ++row) {
            uint8_t bits = font5x8[idx][row];
            for (uint8_t col = 0; col < 5; ++col) {
                if (bits & (1 << (4 - col))) {
                    // 1.5x scale: draw 3x2 block for each pixel
                    int px = dayX + i * (charW + charSpacingDay) + col * scale;
                    int py = dayY + row * scale;
                    // Draw 3x2 block
                    _panel.drawPixel(px, py, 0, 100, 0);
                    _panel.drawPixel(px + 1, py, 0, 100, 0);
                    _panel.drawPixel(px + 2, py, 0, 100, 0);
                    _panel.drawPixel(px, py + 1, 0, 100, 0);
                    _panel.drawPixel(px + 1, py + 1, 0, 100, 0);
                    _panel.drawPixel(px + 2, py + 1, 0, 100, 0);
                }
            }
        }
    }

    // Draw AM/PM in the middle between center and 3 o'clock
    const char* ampmStr = (hour < 12) ? "AM" : "PM";
    uint8_t textLen = 2;
    uint8_t charSpacingAmpm = 1;
    uint8_t textWidth = (5 + charSpacingAmpm) * textLen - charSpacingAmpm;
    uint8_t textHeight = 8;
    int16_t ax = cx + radius / 2 - textWidth / 2;
    int16_t ay = cy - textHeight / 2;
    
    for (uint8_t i = 0; i < textLen; ++i) {
        int idx = fontIndex(ampmStr[i]);
        for (uint8_t row = 0; row < 8; ++row) {
            uint8_t bits = font5x8[idx][row];
            for (uint8_t col = 0; col < 5; ++col) {
                if (bits & (1 << (4 - col))) {
                    _panel.drawPixel(ax + i * (5 + charSpacingAmpm) + col, ay + row, 0, 100, 0);
                }
            }
        }
    }

    // Draw hands in foreground
    float hourAngle = ((hour % 12) + minute / 60.0f) * 30.0f * M_PI / 180.0f;
    float minuteAngle = minute * 6.0f * M_PI / 180.0f;
    float secondAngle = second * 6.0f * M_PI / 180.0f;

    // Draw hour hand (red, thick)
    int16_t hx = cx + (int16_t)((radius * 0.5f) * sin(hourAngle));
    int16_t hy = cy - (int16_t)((radius * 0.5f) * cos(hourAngle));
    _panel.drawLine(cx, cy, hx, hy, 255, 0, 0);
    // Add thickness
    int16_t hx2 = cx + (int16_t)((radius * 0.5f) * sin(hourAngle)) + (int16_t)(cos(hourAngle) * 1);
    int16_t hy2 = cy - (int16_t)((radius * 0.5f) * cos(hourAngle)) + (int16_t)(sin(hourAngle) * 1);
    _panel.drawLine(cx, cy, hx2, hy2, 255, 0, 0);
    int16_t hx3 = cx + (int16_t)((radius * 0.5f) * sin(hourAngle)) - (int16_t)(cos(hourAngle) * 1);
    int16_t hy3 = cy - (int16_t)((radius * 0.5f) * cos(hourAngle)) - (int16_t)(sin(hourAngle) * 1);
    _panel.drawLine(cx, cy, hx3, hy3, 255, 0, 0);

    // Draw minute hand (yellow, thick)
    int16_t mx = cx + (int16_t)((radius * 0.7f) * sin(minuteAngle));
    int16_t my = cy - (int16_t)((radius * 0.7f) * cos(minuteAngle));
    _panel.drawLine(cx, cy, mx, my, 255, 255, 0);
    // Add thickness
    int16_t mx2 = cx + (int16_t)((radius * 0.7f) * sin(minuteAngle)) + (int16_t)(cos(minuteAngle) * 1);
    int16_t my2 = cy - (int16_t)((radius * 0.7f) * cos(minuteAngle)) + (int16_t)(sin(minuteAngle) * 1);
    _panel.drawLine(cx, cy, mx2, my2, 255, 255, 0);
    int16_t mx3 = cx + (int16_t)((radius * 0.7f) * sin(minuteAngle)) - (int16_t)(cos(minuteAngle) * 1);
    int16_t my3 = cy - (int16_t)((radius * 0.7f) * cos(minuteAngle)) - (int16_t)(sin(minuteAngle) * 1);
    _panel.drawLine(cx, cy, mx3, my3, 255, 255, 0);

    // Draw second hand (blue, thick)
    int16_t sx = cx + (int16_t)((radius * 0.9f) * sin(secondAngle));
    int16_t sy = cy - (int16_t)((radius * 0.9f) * cos(secondAngle)) + 4;
    _panel.drawLine(cx, cy, sx, sy, 0, 0, 255);
    // Add thickness
    int16_t sx2 = cx + (int16_t)((radius * 0.9f) * sin(secondAngle)) + (int16_t)(cos(secondAngle) * 1);
    int16_t sy2 = cy - (int16_t)((radius * 0.9f) * cos(secondAngle)) + (int16_t)(sin(secondAngle) * 1) + 4;
    _panel.drawLine(cx, cy, sx2, sy2, 0, 0, 255);
    int16_t sx3 = cx + (int16_t)((radius * 0.9f) * sin(secondAngle)) - (int16_t)(cos(secondAngle) * 1);
    int16_t sy3 = cy - (int16_t)((radius * 0.9f) * cos(secondAngle)) - (int16_t)(sin(secondAngle) * 1) + 4;
    _panel.drawLine(cx, cy, sx3, sy3, 0, 0, 255);
}